//asio 异步api
//参考链接：https://llfc.club/category?catid=225RaiVNI8pFDD5L4m807g7ZwmF#!aid/2O0QikntG7ktdgARRndYzNg8R55

#include <atomic>
#include <cstdint>
#include <memory>
#include <utility>
#include <string>
#include <queue>
#include <boost/asio.hpp>

//为方便异步读写，管理要发送和接收的数据
struct MsgNode
{
    //作为发送节点 字符地址和字符长度
    MsgNode(const char* msg, size_t len):m_total_len(len), m_cur_len(0)
    {
        m_msg = new char[len];
        memcpy(m_msg, msg, len);
    }
    //作为接收节点 字符长度
    MsgNode(size_t len):m_total_len(len), m_cur_len(0)
    {
        m_msg = new char[len];
        memset(m_msg, 0, len);
    }

    ~MsgNode()
    {
        delete[] m_msg;
    }

    size_t m_total_len; //数据总长度
    size_t m_cur_len;   //已处理长度
    char*  m_msg;       //数据指针
};


constexpr uint32_t MAX_SIZE = 1024;

//与服务端建立链接的客户端节点
class Session
{
public:
    Session(std::shared_ptr<boost::asio::ip::tcp::socket> socket);

    //服务端主动链接客户端（很少使用）
    void Connct(const boost::asio::ip::tcp::endpoint& ep);

    //可能产生问题的封装
    void WriteCallBackErr(const boost::system::error_code& ec, size_t bytes, 
            std::shared_ptr<MsgNode> node);
    void WriteToSocketErr(const std::string buf);

    //使用 async_write_some 解决问题，但可能会频繁调用WriteCallBack回调函数
    void WriteCallBack(const boost::system::error_code& ec, size_t bytes);
    void WriteToSocket(const std::string& buf);

    //使用 async_send 减少对回调函数的调用  
    //async_write_some 和 async_send 不可以同时使用，否则依然会出现时序问题
    void WriteAllCallBack(const boost::system::error_code& ec, size_t bytes);
    void WriteTAlloSocket(const std::string& buf);

    //使用 async_read_some  可能会频繁调用ReadCallBack回调函数
    void ReadCallBack(const boost::system::error_code& ec, size_t bytes);
    void ReadFromSocket();

    //使用 async_receive  减少对回调函数的调用
    //async_read_some 和 async_receive 不可以同时使用，否则依然会出现时序问题
    void ReadAllCallBack(const boost::system::error_code& ec, size_t bytes);
    void ReadAllFromSocket();
private:
    std::shared_ptr<boost::asio::ip::tcp::socket> m_socket;
    std::shared_ptr<MsgNode> m_send_node;

    std::queue<std::shared_ptr<MsgNode>> m_send_queue; //发送队列，保证数据的发送顺序
    std::atomic_bool m_send_pending;

    std::shared_ptr<MsgNode> m_recv_node;
    std::atomic_bool m_recv_pending;
};



